package com.asen.libcommon.widget

import android.content.Context
import android.graphics.*
import android.os.Parcel
import android.os.Parcelable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.AccelerateInterpolator
import com.asen.libcommon.R

/**
 * @date   : 2021/11/23
 * @author : asenLiang
 * @e-mail : liangAisiSen@163.com
 * @desc   : 自定义开关
 */

class CommonSwitchView constructor(context: Context, attrs: AttributeSet?) : View(context, attrs) {


    companion object {
        private const val STATE_SWITCH_ON = 4
        private const val STATE_SWITCH_ON2 = 3
        private const val STATE_SWITCH_OFF2 = 2
        private const val STATE_SWITCH_OFF = 1
    }


    private val interpolator = AccelerateInterpolator(2f)
    private val paint = Paint()
    private val sPath = Path()
    private val bPath = Path()
    private val bRectF = RectF()
    private var sAnim = 0f
    private var bAnim = 0f
    private var shadowGradient: RadialGradient? = null
    private var ratioAspect = 0.1f // (0,1]
    private var animationSpeed = 0.1f // (0,1]
    private var state: Int
    private var lastState: Int
    private var isCanVisibleDrawing = false
    private var mOnClickListener: OnClickListener? = null

    // todo：选择后的主题色
    protected var colorPrimary: Int

    // todo：副主题色
    protected var colorPrimaryDark: Int

    // todo：关闭后的条颜色
    private var colorOff: Int

    // todo：圈圈的外圆颜色
    private var colorOffDark: Int

    // todo：阴影的颜色
    private var colorShadow: Int

    // todo：圈圈颜色
    private var colorBar: Int

    // todo：背景颜色
    private var colorBackground: Int

    private var hasShadow: Boolean
    private var sRight = 0f
    private var sCenterX = 0f
    private var sCenterY = 0f
    private var sScale = 0f
    private var bOffset = 0f
    private var bRadius = 0f
    private var bStrokeWidth = 0f
    private var bWidth = 0f
    private var bLeft = 0f
    private var bRight = 0f
    private var bOnLeftX = 0f
    private var bOn2LeftX = 0f
    private var bOff2LeftX = 0f
    private var bOffLeftX = 0f
    private var shadowReservedHeight = 0f

    private var isOpened: Boolean

    /** 点击是否能触发切换动画 true:可以自动触发切换 false:通过调用方法触发切换 */
    var isClickChangeSwitch: Boolean = false

    constructor(context: Context) : this(context, null)

    init {
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        val a = context.obtainStyledAttributes(attrs, R.styleable.CommonSwitchView)
        colorPrimary = a.getColor(R.styleable.CommonSwitchView_SWPrimaryColor, context.resources.getColor(R.color.colorPrimary))
        colorPrimaryDark = a.getColor(R.styleable.CommonSwitchView_SWPrimaryColorDark, context.resources.getColor(R.color.colorPrimaryDark))
        colorOff = a.getColor(R.styleable.CommonSwitchView_SWOffColor, context.resources.getColor(R.color.colorPrimaryDark))
        colorOffDark = a.getColor(R.styleable.CommonSwitchView_SWOffColorDark, context.resources.getColor(R.color.white_FFFFFF))
        colorShadow = a.getColor(R.styleable.CommonSwitchView_SWShadowColor, context.resources.getColor(R.color.white_FFFFFF))
        colorBar = a.getColor(R.styleable.CommonSwitchView_SWBarColor, context.resources.getColor(R.color.white_FFFFFF))
        colorBackground = a.getColor(R.styleable.CommonSwitchView_SWBgColor, context.resources.getColor(R.color.colorPrimaryDark))
        ratioAspect = a.getFloat(R.styleable.CommonSwitchView_SWRatioAspect, 0.68f)
        hasShadow = a.getBoolean(R.styleable.CommonSwitchView_SWHasShadow, true)
        isOpened = a.getBoolean(R.styleable.CommonSwitchView_SWIsOpened, false)
        state = if (isOpened) STATE_SWITCH_ON else STATE_SWITCH_OFF
        lastState = state
        a.recycle()
    }


    fun setColor(
        newColorPrimary: Int = colorPrimary,
        newColorPrimaryDark: Int = colorPrimaryDark,
        newColorOff: Int = colorOff,
        newColorOffDark: Int = colorOffDark,
        newColorShadow: Int = colorShadow,
        newColorBar: Int = colorBar,
        newColorBackground: Int = colorBackground
    ) {
        colorPrimary = newColorPrimary
        colorPrimaryDark = newColorPrimaryDark
        colorOff = newColorOff
        colorOffDark = newColorOffDark
        colorShadow = newColorShadow
        colorBar = newColorBar
        colorBackground = newColorBackground
        invalidate()
    }

    /**
     * 设置阴影
     * @param shadow Boolean
     */
    fun setShadow(shadow: Boolean) {
        hasShadow = shadow
        invalidate()
    }

    /** 获取状态 */
    fun getOpened(): Boolean = isOpened

    /** 设置状态 */
    fun setOpened(isOpened: Boolean) {
        val wishState = if (isOpened) STATE_SWITCH_ON else STATE_SWITCH_OFF
        if (wishState == state) return
        refreshState(wishState)
    }

    fun toggleSwitch(isOpened: Boolean) {
        val wishState = if (isOpened) STATE_SWITCH_ON else STATE_SWITCH_OFF
        if (wishState == state) return
        if (wishState == STATE_SWITCH_ON && (state == STATE_SWITCH_OFF || state == STATE_SWITCH_OFF2)
            || wishState == STATE_SWITCH_OFF && (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2)
        ) { sAnim = 1f }
        bAnim = 1f
        refreshState(wishState)
    }

    /**
     * 刷新状态
     * @param newState 当前状态
     * @param isTouch 判断是否由onTouch触发的刷新方法
     */
    private fun refreshState(newState: Int, isTouch: Boolean = false) {
        if (!isOpened && newState == STATE_SWITCH_ON) {
            isOpened = true
        } else if (isOpened && newState == STATE_SWITCH_OFF) {
            isOpened = false
        }
        lastState = state
        state = newState
        if (isTouch) {
            // todo:判断是否允许通过点击刷新按钮
            if (isClickChangeSwitch) postInvalidate()
        } else postInvalidate()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var resultWidth: Int
        if (widthMode == MeasureSpec.EXACTLY) {
            resultWidth = widthSize
        } else {
            resultWidth =
                ((35 * resources.displayMetrics.density + 0.5f).toInt() + paddingLeft + paddingRight)
            if (widthMode == MeasureSpec.AT_MOST) {
                resultWidth = Math.min(resultWidth, widthSize)
            }
        }
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        var resultHeight: Int
        if (heightMode == MeasureSpec.EXACTLY) {
            resultHeight = heightSize
        } else {
            resultHeight = (resultWidth * ratioAspect).toInt() + paddingTop + paddingBottom
            if (heightMode == MeasureSpec.AT_MOST) {
                resultHeight = Math.min(resultHeight, heightSize)
            }
        }
        setMeasuredDimension(resultWidth, resultHeight)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        isCanVisibleDrawing = w > paddingLeft + paddingRight && h > paddingTop + paddingBottom
        if (isCanVisibleDrawing) {
            val actuallyDrawingAreaWidth = w - paddingLeft - paddingRight
            val actuallyDrawingAreaHeight = h - paddingTop - paddingBottom
            val actuallyDrawingAreaLeft: Int
            val actuallyDrawingAreaRight: Int
            val actuallyDrawingAreaTop: Int
            val actuallyDrawingAreaBottom: Int
            if (actuallyDrawingAreaWidth * ratioAspect < actuallyDrawingAreaHeight) {
                actuallyDrawingAreaLeft = paddingLeft
                actuallyDrawingAreaRight = w - paddingRight
                val heightExtraSize =
                    (actuallyDrawingAreaHeight - actuallyDrawingAreaWidth * ratioAspect).toInt()
                actuallyDrawingAreaTop = paddingTop + heightExtraSize / 2
                actuallyDrawingAreaBottom = height - paddingBottom - heightExtraSize / 2
            } else {
                val widthExtraSize =
                    (actuallyDrawingAreaWidth - actuallyDrawingAreaHeight / ratioAspect).toInt()
                actuallyDrawingAreaLeft = paddingLeft + widthExtraSize / 2
                actuallyDrawingAreaRight = width - paddingRight - widthExtraSize / 2
                actuallyDrawingAreaTop = paddingTop
                actuallyDrawingAreaBottom = height - paddingBottom
            }
            shadowReservedHeight = ((actuallyDrawingAreaBottom - actuallyDrawingAreaTop) * 0.07f)
            val sLeft = actuallyDrawingAreaLeft.toFloat()
            val sTop = actuallyDrawingAreaTop + shadowReservedHeight
            sRight = actuallyDrawingAreaRight.toFloat()
            val sBottom = actuallyDrawingAreaBottom - shadowReservedHeight
            val sHeight = sBottom - sTop
            sCenterX = (sRight + sLeft) / 2
            sCenterY = (sBottom + sTop) / 2
            bLeft = sLeft
            bWidth = sBottom - sTop
            bRight = sLeft + bWidth
            val halfHeightOfS = bWidth / 2 // OfB
            bRadius = halfHeightOfS * 0.95f
            bOffset = bRadius * 0.2f // offset of switching
            bStrokeWidth = (halfHeightOfS - bRadius) * 2
            bOnLeftX = sRight - bWidth
            bOn2LeftX = bOnLeftX - bOffset
            bOffLeftX = sLeft
            bOff2LeftX = bOffLeftX + bOffset
            sScale = 1 - bStrokeWidth / sHeight
            sPath.reset()
            val sRectF = RectF()
            sRectF.top = sTop
            sRectF.bottom = sBottom
            sRectF.left = sLeft
            sRectF.right = sLeft + sHeight
            sPath.arcTo(sRectF, 90f, 180f)
            sRectF.left = sRight - sHeight
            sRectF.right = sRight
            sPath.arcTo(sRectF, 270f, 180f)
            sPath.close()
            bRectF.left = bLeft
            bRectF.right = bRight
            bRectF.top = sTop + bStrokeWidth / 2 // bTop = sTop
            bRectF.bottom = sBottom - bStrokeWidth / 2 // bBottom = sBottom
            val bCenterX = (bRight + bLeft) / 2
            val bCenterY = (sBottom + sTop) / 2
            val red = colorShadow shr 16 and 0xFF
            val green = colorShadow shr 8 and 0xFF
            val blue = colorShadow and 0xFF
            shadowGradient = RadialGradient(
                bCenterX, bCenterY, bRadius, Color.argb(200, red, green, blue),
                Color.argb(25, red, green, blue), Shader.TileMode.CLAMP
            )
        }
    }

    private fun calcBPath(percent: Float) {
        bPath.reset()
        bRectF.left = bLeft + bStrokeWidth / 2
        bRectF.right = bRight - bStrokeWidth / 2
        bPath.arcTo(bRectF, 90f, 180f)
        bRectF.left = bLeft + percent * bOffset + bStrokeWidth / 2
        bRectF.right = bRight + percent * bOffset - bStrokeWidth / 2
        bPath.arcTo(bRectF, 270f, 180f)
        bPath.close()
    }

    private fun calcBTranslate(percent: Float): Float {
        var result = 0f
        when (state - lastState) {
            1 -> if (state == STATE_SWITCH_OFF2) {
                result = bOffLeftX // off -> off2
            } else if (state == STATE_SWITCH_ON) {
                result = bOnLeftX - (bOnLeftX - bOn2LeftX) * percent // on2 -> on
            }
            2 -> if (state == STATE_SWITCH_ON) {
                result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent // off2 -> on
            } else if (state == STATE_SWITCH_ON2) {
                result = bOn2LeftX - (bOn2LeftX - bOffLeftX) * percent // off -> on2
            }
            3 -> result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent // off -> on
            -1 -> if (state == STATE_SWITCH_ON2) {
                result = bOn2LeftX + (bOnLeftX - bOn2LeftX) * percent // on -> on2
            } else if (state == STATE_SWITCH_OFF) {
                result = bOffLeftX // off2 -> off
            }
            -2 -> if (state == STATE_SWITCH_OFF) {
                result = bOffLeftX + (bOn2LeftX - bOffLeftX) * percent // on2 -> off
            } else if (state == STATE_SWITCH_OFF2) {
                result = bOff2LeftX + (bOnLeftX - bOff2LeftX) * percent // on -> off2
            }
            -3 -> result = bOffLeftX + (bOnLeftX - bOffLeftX) * percent // on -> off
            0 -> if (state == STATE_SWITCH_OFF) {
                result = bOffLeftX //  off -> off
            } else if (state == STATE_SWITCH_ON) {
                result = bOnLeftX // on -> on
            }
            else -> if (state == STATE_SWITCH_OFF) {
                result = bOffLeftX
            } else if (state == STATE_SWITCH_ON) {
                result = bOnLeftX
            }
        }
        return result - bOffLeftX
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (!isCanVisibleDrawing) return
        paint.isAntiAlias = true
        val isOn = state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2
        // Draw background
        paint.style = Paint.Style.FILL
        paint.color = if (isOn) colorPrimary else colorOff
        canvas.drawPath(sPath, paint)
        sAnim = if (sAnim - animationSpeed > 0) sAnim - animationSpeed else 0f
        bAnim = if (bAnim - animationSpeed > 0) bAnim - animationSpeed else 0f
        val dsAnim = interpolator.getInterpolation(sAnim)
        val dbAnim = interpolator.getInterpolation(bAnim)
        // Draw background animation
        val scale = sScale * if (isOn) dsAnim else 1 - dsAnim
        val scaleOffset = (sRight - sCenterX - bRadius) * if (isOn) 1 - dsAnim else dsAnim
        canvas.save()
        canvas.scale(scale, scale, sCenterX + scaleOffset, sCenterY)
        paint.color = colorBackground
        canvas.drawPath(sPath, paint)
        canvas.restore()
        // To prepare center bar path
        canvas.save()
        canvas.translate(calcBTranslate(dbAnim), shadowReservedHeight)
        val isState2 = state == STATE_SWITCH_ON2 || state == STATE_SWITCH_OFF2
        calcBPath(if (isState2) 1 - dbAnim else dbAnim)
        // Use center bar path to draw shadow
        if (hasShadow) {
            paint.style = Paint.Style.FILL
            paint.shader = shadowGradient
            canvas.drawPath(bPath, paint)
            paint.shader = null
        }
        canvas.translate(0f, -shadowReservedHeight)
        // draw bar
        canvas.scale(0.98f, 0.98f, bWidth / 2, bWidth / 2)
        paint.style = Paint.Style.FILL
        paint.color = colorBar
        canvas.drawPath(bPath, paint)
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = bStrokeWidth * 0.5f
        paint.color = if (isOn) colorPrimaryDark else colorOffDark
        canvas.drawPath(bPath, paint)
        canvas.restore()
        paint.reset()
        if (sAnim > 0 || bAnim > 0) invalidate()
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if ((state == STATE_SWITCH_ON || state == STATE_SWITCH_OFF) && sAnim * bAnim == 0f) {
            when (event.action) {
                MotionEvent.ACTION_DOWN -> return true
                MotionEvent.ACTION_UP -> {
                    lastState = state
                    bAnim = 1f
                    if (state == STATE_SWITCH_OFF) {
                        refreshState(STATE_SWITCH_OFF2, true)
                        listener.toggleToOn(this)
                    } else if (state == STATE_SWITCH_ON) {
                        refreshState(STATE_SWITCH_ON2, true)
                        listener.toggleToOff(this)
                    }
                    if (mOnClickListener != null) {
                        mOnClickListener!!.onClick(this)
                    }
                }
            }
        }
        return super.onTouchEvent(event)
    }

    override fun setOnClickListener(l: OnClickListener?) {
        super.setOnClickListener(l)
        mOnClickListener = l
    }

    interface OnStateChangedListener {
        fun toggleToOn(view: CommonSwitchView?)
        fun toggleToOff(view: CommonSwitchView?)
    }

    private var listener: OnStateChangedListener = object : OnStateChangedListener {
        override fun toggleToOn(view: CommonSwitchView?) {
            toggleSwitch(true)
            mStateChange?.invoke(true)
        }

        override fun toggleToOff(view: CommonSwitchView?) {
            toggleSwitch(false)
            mStateChange?.invoke(false)
        }
    }

    public override fun onSaveInstanceState(): Parcelable {
        val superState = super.onSaveInstanceState()
        val ss = SavedState(superState)
        ss.isOpened = isOpened
        return ss
    }

    private var mStateChange: ((state: Boolean) -> Unit)? = null

    // 监听状态改变
    fun setStateChangeListener(callback: ((state: Boolean) -> Unit)) {
        mStateChange = callback
    }

    public override fun onRestoreInstanceState(state: Parcelable) {
        val ss = state as SavedState
        super.onRestoreInstanceState(ss.superState)
        isOpened = ss.isOpened
        this.state = if (isOpened) STATE_SWITCH_ON else STATE_SWITCH_OFF
        invalidate()
    }

    private class SavedState : BaseSavedState {
        var isOpened = false

        internal constructor(superState: Parcelable?) : super(superState) {}

        private constructor(`in`: Parcel) : super(`in`) { isOpened = 1 == `in`.readInt() }

        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeInt(if (isOpened) 1 else 0)
        }

        override fun describeContents(): Int { return 0 }

        companion object {
            val CREATOR: Parcelable.Creator<SavedState?> =
                object : Parcelable.Creator<SavedState?> {
                    override fun createFromParcel(`in`: Parcel): SavedState? { return SavedState(`in`) }

                    override fun newArray(size: Int): Array<SavedState?> { return arrayOfNulls(size) }
                }
        }
    }


}